package com.geostar.constructionland.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;

@Slf4j
public class MultithreadUtil {
    private CountDownLatch threadsSignal;
    // 每个线程处理的数据量
    private int count = 100;
    // 定义线程池数量
    private ExecutorService execPool;
    private Map<Integer, List<Object>> rsltMap = new ConcurrentHashMap<Integer, List<Object>>();

    public MultithreadUtil(){}

    /**
     * 构造函数初始化
     * @param execPool 线程池
     * @param count 每个线程处理的数据量
     */
    public MultithreadUtil(ExecutorService execPool, int count) {
        this.execPool = execPool;
        this.count = count;
    }

    /**
     * 多线程批量处理数据
     * @param classPath 类的路径
     * @param methodName 方法名称
     * @param paramClasss 方法参数类型数组
     * @param params 方法参数的集合，若方法参数为可变参数，Object[]的长度必须为1，
     * 		例如：方法method(int... args)，则
     * 		Class[] paramClasss = {int[].class};
     * 		int[] arr = {i, j, k};
     * 		Object[] obj = {arr};
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List<Object> processingDataBatch(String classPath,
                                            String methodName, Class[] paramClasss, List<Object[]> params) {
        return processingDataBatch(null, classPath, methodName, paramClasss, params);
    }

    /**
     * 多线程批量处理数据
     * @param superclassPath 父类的路径
     * @param classPath 类的路径
     * @param methodName 方法名称
     * @param paramClasss 方法参数类型数组
     * @param params 方法参数的集合，若方法参数为可变参数，Object[]的长度必须为1，
     * 		例如：方法method(int... args)，则
     * 		Class[] paramClasss = {int[].class};
     * 		int[] arr = {i, j, k};
     * 		Object[] obj = {arr};
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List<Object> processingDataBatch(String superclassPath, String classPath,
                                            String methodName, Class[] paramClasss, List<Object[]> params) {
        List<Object> resultList = null;
        try {
            log.info("========== 多线程批量处理数据开始 ==========");
            long startTime = new Date().getTime();
            int paramSize = params.size();
            log.info("========== 方法参数的集合大小为：{} ==========", paramSize);
            int num = 1;
            if (paramSize < count) {
                threadsSignal = new CountDownLatch(1);
                execPool.execute(new ProcessingData(superclassPath, classPath, methodName,
                        paramClasss, params, num));
            } else {
                // 拆分数据
                List<List<Object[]>> listArr = createList(params, count);
                // 设置容量
                int listArrSize = listArr.size();
                threadsSignal = new CountDownLatch(listArrSize);
                for (List<Object[]> list : listArr) {
                    execPool.execute(new ProcessingData(superclassPath, classPath, methodName,
                            paramClasss, list, num));
                    num++;
                }
            }
            threadsSignal.await();
            if (rsltMap.size() > 0) {
                resultList = new ArrayList<Object>();
                List<Map.Entry<Integer, List<Object>>> rsltList = new ArrayList<Map.Entry<Integer, List<Object>>>(
                        rsltMap.entrySet());
                Collections.sort(rsltList,
                        new Comparator<Map.Entry<Integer, List<Object>>>() {
                            // 升序排序
                            @Override
                            public int compare(Entry<Integer, List<Object>> o1,
                                               Entry<Integer, List<Object>> o2) {
                                return o1.getKey().compareTo(o2.getKey());
                            }
                        });

                for (Map.Entry<Integer, List<Object>> map : rsltList) {
                    resultList.addAll(map.getValue());
                }
            }
            long endTime = new Date().getTime();
            log.info("========== 多线程批量处理数据结束，用时：{}秒 ==========", (endTime - startTime) / 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList;
    }

    /**
     * 数据拆分
     *
     * @param targe
     * @param size
     * @return
     */
    private static List<List<Object[]>> createList(List<Object[]> targe, int size) {
        List<List<Object[]>> listArr = new ArrayList<List<Object[]>>();
        // 获取被拆分的集合个数
        int arrSize = targe.size() % size == 0 ? targe.size() / size : targe
                .size() / size + 1;
        for (int i = 0; i < arrSize; i++) {
            List<Object[]> sub = new ArrayList<Object[]>();
            // 把指定索引数据放入到list中
            for (int j = i * size; j <= size * (i + 1) - 1; j++) {
                if (j <= targe.size() - 1) {
                    sub.add(targe.get(j));
                }
            }
            listArr.add(sub);
        }
        return listArr;
    }

    class ProcessingData extends Thread {
        String superclassPath, classPath, methodName;
        @SuppressWarnings("rawtypes")
        Class[] paramClasss;
        List<Object[]> params = new ArrayList<Object[]>();
        int num;

        @SuppressWarnings("rawtypes")
        public ProcessingData(String superclassPath, String classPath,
                              String methodName, Class[] paramClasss, List<Object[]> list,
                              int num) {
            this.superclassPath = superclassPath;
            this.classPath = classPath;
            this.methodName = methodName;
            this.paramClasss = paramClasss;
            params = list;
            this.num = num;
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        public void run() {
            try {
                List<Object> resultList = new ArrayList<Object>();
                Class superclazz = null;
                Class clazz = Class.forName(classPath);
                Object classObj = clazz.newInstance();
                Method method = null;
                if(StringUtils.isNotBlank(superclassPath)) {
                    superclazz = Class.forName(superclassPath);
                    method = superclazz.getDeclaredMethod(methodName, paramClasss);
                } else {
                    method = clazz.getDeclaredMethod(methodName, paramClasss);
                }
                for(Object[] objs : params) {
                    Object obj = method.invoke(classObj, objs);
                    if(obj != null) {
                        resultList.add(obj);
                    }
                }
                rsltMap.put(num, resultList);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadsSignal.countDown();
            }
        }
    }
}
